package cn.imatu.framework.knife4j.micro.servlet;

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;

import java.util.Set;

/**
 * @author shenguangyang
 */
@Slf4j
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class Knife4jDocketRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    private static Environment environment;

    @Override
    public void registerBeanDefinitions(@NotNull AnnotationMetadata annotationMetadata, @NotNull BeanDefinitionRegistry registry) {
//        Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(ApiDocket.class.getName());
        String basePackage = ClassUtils.getPackageName(annotationMetadata.getClassName());
        log.info("开始扫描所有 {} 注解, basePackage : {}", ApiGroupConfig.class.getSimpleName(), basePackage);

        Reflections reflection = new Reflections(new ConfigurationBuilder()
            .setScanners(Scanners.TypesAnnotated)
            .forPackage(basePackage)
            .filterInputsBy(new FilterBuilder().includePackage( "BOOT-INF.classes." + basePackage).includePackage(basePackage)));
        Set<Class<?>> targetClassList = reflection.getTypesAnnotatedWith(ApiGroupConfig.class);
        log.info("扫描标注 {} 注解类数量: {}", ApiGroupConfig.class.getSimpleName(), targetClassList.size());
        targetClassList.forEach(targetClass -> {
            ApiGroupConfig annot = targetClass.getAnnotation(ApiGroupConfig.class);
            String alias = targetClass.getSimpleName() + "ApiDocket";
            // BeanDefinitionBuilder通过ApiDocketFactoryBean这个类来生成BeanDefinition
            BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(GroupedOpenApiFactoryBean.class);
            // 通过BeanDefinitionBuilder给beanDefinition增加属性
            definitionBuilder.addPropertyValue("beanName", alias);
            definitionBuilder.addPropertyValue("apiDocket", annot);
            definitionBuilder.addPropertyValue("environment", environment);
            definitionBuilder.addPropertyValue("basePackage", ClassUtils.getPackageName(targetClass));

            // 用Builder获取实际的BeanDefinition
            AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
            beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_NO);
            beanDefinition.setAutowireCandidate(true);
            // 创建一个Bean定义的持有者
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, alias, new String[] { alias });
            // 这里就是将Bean注册到Spring容器中
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
            log.info("准备动态注册 knife4j-docket 就绪, beanName: {}", alias);
        });
    }

    @Override
    public void setEnvironment(@NotNull Environment environment) {
        Knife4jDocketRegistrar.environment = environment;
    }
}
